package l1cache

import (
	"reflect"
	"sync"
	"time"

	"github.com/gocpp/log"
	"go.uber.org/zap"
)

//1级缓存
type MemoryMgr struct {
	sync.Once

	//每日统计数据
	mstDayList sync.Map
	//可根据需要自行扩展别的数据模型
	//数据上次回写时间
	writeBackTime time.Time
	//数据上次淘汰时间
	lruBackTime time.Time

	// iFactoryModel model.IFactoryModel //业务落地数据
}

func NewMemoryMgr() *MemoryMgr {
	_this := &MemoryMgr{
		// iFactoryModel: model.NewFactoryModel(&gin.Context{}),
	}
	_this.Do(func() {
		go func() {
			ticker := time.NewTicker(1 * time.Minute) //1分钟定时
			for range ticker.C {
				_this.LoadToRedis()
			}
		}()
		go func() {
			ticker := time.NewTicker(1 * time.Hour) //1小时触发一次lru
			for range ticker.C {
				_this.LruMemory()
			}
		}()
	})
	return _this
}

func (_this *MemoryMgr) GetMstatDayStat(key string) (result *mstatDayStat) {
	var (
		ok    bool
		value interface{}
	)
	value, ok = _this.mstDayList.Load(key)
	if !ok {
		result = newMstatDayStat()
		value, ok = _this.mstDayList.LoadOrStore(key, result)
		if ok {
			result = value.(*mstatDayStat)
		}
	} else {
		result = value.(*mstatDayStat)
	}
	return
}

//控制数据回写
func (_this *MemoryMgr) LoadToRedis() bool {

	oldwriteBackTime := _this.writeBackTime
	_this.writeBackTime = time.Now()
	log.Info("LoadToRedis start", zap.Any("oldwriteBackTime", oldwriteBackTime), zap.Any("WriteBackTime", _this.writeBackTime))

	//mstatday 写入redis
	{
		mdata := make(map[string]map[string]int64)
		_this.mstDayList.Range(func(key, value interface{}) bool {
			obj := value.(*mstatDayStat)
			if val := obj.Reset(); len(val) > 0 {
				mdata[key.(string)] = val
			}
			return true
		})
		log.Info("---Mstat_Type_Day", zap.Any("mdata", mdata))
		// _this.iFactoryModel.GetIMstat().GetInterface(public.Mstat_Type_Day).MultiIncrby(mdata) //业务落地数据
	}

	return true
}

//数据清理
func (_this *MemoryMgr) LruMemory() bool {

	oldlruBackTime := _this.lruBackTime
	_this.lruBackTime = time.Now()
	log.Info("LoadToRedis start", zap.Any("oldlruBackTime", oldlruBackTime), zap.Any("lruBackTime", _this.lruBackTime))

	{
		_this.mstDayList.Range(func(key, value interface{}) bool {
			obj := value.(*mstatDayStat)
			if obj.CheckLru() {
				_this.mstDayList.Delete(key)
				obj = nil
			}
			return true
		})
	}

	return true
}

//反射需要使用的是值，而不是指针
func getAllField(i interface{}) map[string]int64 {
	t := reflect.TypeOf(i)
	v := reflect.ValueOf(i)
	values := make(map[string]int64, t.NumField())
	for i := 0; i < t.NumField(); i++ {
		tf := t.Field(i)
		vf := v.Field(i)
		name := tf.Tag.Get("json")
		switch vf.Kind() {
		case reflect.Int32, reflect.Int64:
			if vf.Int() > 0 {
				values[name] = vf.Int()
			}
		}
	}
	return values
}
